package com.ahdms.es.util;

import com.ahdms.es.bean.LdapServerInfo;
import com.ahdms.es.config.PropertiesConfig;
import com.ahdms.es.engine.IKICertVerifyEngine;
import com.ahdms.es.engine.PKICertVerifyEngine;
import com.ahdms.es.engine.X509CertVerifyEngine;
import com.ahdms.es.gm.constant.IKIObjectIdentifiers;
import com.ahdms.es.result.VerifyResult;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.x509.*;

import java.util.Arrays;
import java.util.Date;

/**
 * @author qinxiang
 * @date 2021-04-14 16:55
 */
public class LdapUtils {

    private static final String CN = "cn=";

    private static final String LDAP_PRE = "ldap://";

    public static Date queryLdap(Certificate cert){
        ASN1Encodable pkAlgorithmIdentifier = cert.getSubjectPublicKeyInfo().getAlgorithm().getParameters();
        if (IKIObjectIdentifiers.sm_puk_combined.equals(pkAlgorithmIdentifier)) {
            return queryLdapByIki(cert, PropertiesConfig.getPkmCert());
        } else if (IKIObjectIdentifiers.sm_2.equals(pkAlgorithmIdentifier)) {
            return queryLdapByPki(cert);
        } else {
            throw new IllegalArgumentException("算法标识有误."+pkAlgorithmIdentifier);
        }
    }

    public static Date queryLdapByIki(Certificate cert, Certificate rootCert) {
        try {
            String irlPoint = CertTools.getIRLPoint(cert);
            LdapServerInfo ldapServerInfo = crlToLdapServerInfo(irlPoint);
            CertificateList certificateList = LdapQuery.CRLDownload(ldapServerInfo);
            if(!verifySign(certificateList, rootCert)){
                throw new RuntimeException("crl验签失败..");
            }
            Date revokeTime = isRevoke(certificateList, cert);
            if (null != revokeTime) {
                return revokeTime;
            }
            //判断此crl是增量还是全量
            Extension deltaCRL = certificateList.getTBSCertList().getExtensions().getExtension(Extension.deltaCRLIndicator);
            if (deltaCRL != null) {
                //修改为全量的查询
                updateLdapServerInfo(ldapServerInfo, deltaCRL);
                CertificateList fullCrlList = LdapQuery.CRLDownload(ldapServerInfo);
                if(!verifySign(fullCrlList, rootCert)){
                    throw new RuntimeException("crl验签失败..");
                }
                return isRevoke(fullCrlList, cert);
            }
            return null;
        } catch (Exception e) {

        }
        return null;

    }

    public static Date queryLdapByPki(Certificate cert) {
        try {
            String irlPoint = CertTools.getIRLPoint(cert);
            LdapServerInfo ldapServerInfo = crlToLdapServerInfo(irlPoint);
            CertificateList certificateList = LdapQuery.CRLDownload(ldapServerInfo);

            Date revokeTime = isRevoke(certificateList, cert);
            if (null != revokeTime) {
                return revokeTime;
            }
            //判断此crl是增量还是全量
            Extension deltaCRL = certificateList.getTBSCertList().getExtensions().getExtension(Extension.deltaCRLIndicator);
            if (deltaCRL != null) {
                //修改为全量的查询
                updateLdapServerInfo(ldapServerInfo, deltaCRL);
                CertificateList fullCrlList = LdapQuery.CRLDownload(ldapServerInfo);

                return isRevoke(fullCrlList, cert);
            }
            return null;
        } catch (Exception e) {

        }
        return null;
    }

    private static void updateLdapServerInfo(LdapServerInfo ldapServerInfo, Extension deltaCRL) {
        String crlNum = CRLNumber.getInstance(deltaCRL.getExtnValue().getOctets()).getCRLNumber().toString();
        String filter = CN + crlNum;
        String searchDn = new StringBuffer(filter).append(",").append(ldapServerInfo.getSearchDn()).toString();
        ldapServerInfo.setSearchDn(searchDn);
        ldapServerInfo.setAttrs(new String[]{filter});
    }

    private static Date isRevoke(CertificateList crl, Certificate cert) {

        return Arrays.stream(crl.getRevokedCertificates())
                .filter(crlEntry -> cert.getSerialNumber().getValue().equals(crlEntry.getUserCertificate().getValue()))
                .findFirst()
                .map(TBSCertList.CRLEntry::getRevocationDate)
                .map(Time::getDate)
                .orElse(null);

    }

    private static boolean verifySign(CertificateList crl, Certificate rootCert) {
        return IKICertUtils.validateCrlSign(crl, rootCert);
    }


    public static LdapServerInfo crlToLdapServerInfo(String crlUrl) {
        if (crlUrl.startsWith(LDAP_PRE)) {
            String searchDn = crlUrl.substring(crlUrl.lastIndexOf("/") + 1);
            String ldapAddress = crlUrl.substring(0, crlUrl.lastIndexOf("/"));
            return LdapServerInfo.builder().address(ldapAddress).searchDn(searchDn).attrs(searchDn.split(",")).build();
        }
        throw new RuntimeException("crl url error.." + crlUrl);
    }

}
